#ifndef PPEXPAND_H_INCLUDED
#define PPEXPAND_H_INCLUDED

/*******************************************************/
/**                     Helpers                       **/
/*******************************************************/

struct CPass { CPass(const std::initializer_list<int>&) {} };

template < class Arg >
using FunctionTemplate_1 = void(*)(const Arg&);

template < class T > struct dummy {};
template < class T >
using FunctionTemplate_0 = void (*)( dummy<T> );

template < class T, FunctionTemplate_1<T> Func >
struct WrapFunctionTemplate_1
{
    static void call(const T& t) { Func(t); }
};

template < class T, FunctionTemplate_0< T > Func >
struct WrapFunctionTemplate_0
{
    static void call() { Func( dummy<T>() ); }
};

#define TEMPLATE_FUNCTION_ALIAS_1(Function, Name) template < class Arg > using Name = WrapFunctionTemplate_1< Arg, Function >;
#define TEMPLATE_FUNCTION_ALIAS_0(Function, Name) template < class T > using Name = WrapFunctionTemplate_0< T, Function >;

/*******************************************************/
/**                Param expand function              **/
/*******************************************************/

template < class StaticClass, class ...Types >
void expand(const Types&... args)
{
    CPass{ (StaticClass::call(args), 0)... };
}

template < class Functor, class ...Types >
void expand(const Functor& functor, const Types&... args)
{
    CPass{ (functor(args), 0)... };
}

template < template < class > class StaticFuncTmpl, class ...Types >
void expand(const Types&... args)
{
    CPass{ (StaticFuncTmpl<Types>::call(args), 0)... };
}

/*******************************************************/
/**          Template argument expand function        **/
/*******************************************************/

template < class StaticClass, class... Types >
void expandTypes()
{
    CPass{ (StaticClass::template call<Types>(), 0)... };
}

template < class... Types, class Functor >
void expandTypes(const Functor& functor)
{
    CPass{ (functor.template operator()<Types>(), 0)... };
}

template < template < class > class StaticFuncTmpl, class ...Types >
void expandTypes()
{
    CPass{ (StaticFuncTmpl<Types>::call(), 0)... };
}

#endif // PPEXPAND_H_INCLUDED
